本文主要介绍Python3的基本概念、语言机制和程序结构等知识。

基础知识

面向对象 & 动态类型

  • python是一门面向对象的语言,所有出现的值都是对象,都具有类型(可通过type()查看)。
  • python中对象有类型,变量无类型:变量相当于指针,只是对象的一个引用,用来访问对象的值;就像一个标签,贴到哪个对象上就代表哪个对象,可随意更改其指向的对象,因此也就没有固定类型。具体见python变量的绑定(binding)机制
  • Python变量也可以说是动态类型的:变量类型是在运行过程中决定的,系统会自动根据变量名绑定所绑定的对象来推断其类型,且变量的类型在程序中可随意更改。这样做的好处是为程序带来了灵活性,但坏处是增加了程序调试难度(较难明确变量类型),且变量很容易被误覆盖(没有关于类型的赋值限制)。

并非解释型语言

  • 常有人说python是解释型语言,其是并不是这样的。python和java类似,其实是先编译再解释类型的语言,python会将源代码文件编译为pyc文件(在项目文件夹的__pycache__/下),然后再在虚拟机上解释运行。更详细的介绍可见关于编译型和解释型语言

数的类型

  • int类型(signed intergers,有符号整数):python3中int就是长整型,且python对大整数进行了特殊处理,因此一般不用担心整数溢出问题。
  • python中int是动态长度的,即内存中并非严格的4Byte,会根据数的大小动态变化。(不过也在4Byte左右,估算时可以就用4Byte来算)。
  • float类型(floating point real values):在内存中按4Byte来存储,采用科学计数法的表示形式,最大值在在e+308级。
  • complex类型(complex numbers):暂时还没用到过。
  • 数之间的转化直接调用构造函数即可,eg. int()float()

float的误差问题

  • 在计算机中数都是以二进制的形式存储的,因此十进制小数转化为二进制时会存在误差,eg. 0.1的二进制表示为0.000110011…(无限小数,无法精确表示)
  • 虽然,但还是要明确:每次使用浮点运算都是有可能引入舍入误差的。如:

    1
    2
    3
    4
    >>> 0.1+0.2
    0.30000000000000004
    >>> 10/3
    3.3333333333333335
  • 不过大多数情况下误差是很小的(python浮点数精度为小数点后16位,到17位后才开始不准确),完全满足日常的计算需求。且对于数比较相等、比大小等情况,python内部也会对其处理,因此并不会有影响。

  • 对于实在特殊的精确计算要求,有如下解决办法:
  1. 使用decimal模块,其实现了十进制的运算,因此不存在浮点误差;
  2. 使用fractions模块,其会按照分数的规则来计算,也不存在误差;
  3. 使用numpy等其他科学计算包
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import decimal
    a = deciamal.Decimal("10.0")
    b = decimal.Decimal("3")
    print(a/b)
    # 3.333333333333333333333333333

    from fractions import Fraction
    print(Fraction(10, 3)) # 分数10/3
    print(Fraction(10, 8)) # 分数5/4
    print(Fraction(5, 3))
    # 10/3
    # 5/4
    # 5/3

基本运算

算数运算符

  • 只要运算的两方有一方是浮点数,那么结果为浮点数;
  • 对于除法运算/,其结果一定位浮点数。
描述 对应运算符
+
-
*
/
取余 %
取商 math.floor(x%y)divmod(x, y)[0]
取绝对值 abs()
取整 math.floor()(直接截断到整数位)
控制小数位数/精度 round()或字符串格式化
指数运算 **
开平方 sqrt()


其他运算符

描述 对应运算符
boolean或 or
boolean与 and
boolean非 not x
是/不是其中的成员 in, not in
两个对象是否为同一个 is, is not
比较 ==, !=, <. <=, >, >=
正负号 +x, -x
按位与(转为二进制后计算) &
按位异或 ^
按位翻转 ~x
左移y位 x<<y
右移y位 x>>y
  • 按位或:|

注释写法

  • 注释
    单行注释:#
    多行注释:三对单/双引号,’’’xxx’’’或”””xxx”””

python3和python2区别

1、输出: python3是print(xxx), python2是print xxx
2、输入: 从键盘输入字符不同;
3、自定义class:python2要手动继承object变量,python3则自动继承了。
其他地方暂时还没碰到过。

语句

  • if语句
    1
    2
    3
    4
    5
    6
    if a==5:
    xxx
    elif a==10:
    xxx
    else:
    xxx

采用四个空格的缩进来表示语句块开始,删去缩进表示语句块结束;不要忘了冒号。
三元表达式:A = Y if X else Z

  • for语句
    1
    2
    3
    str="hello"
    for i in range(len(str)):
    print(str[i],end=' ') #输出为h e l l o

同样注意冒号,缩进

  • for else语句

    1
    2
    3
    4
    5
    for i in range(1,10,2):
    #语句块
    else :
    #for循环正常结束会执行
    #break直接跳出,不会执行else
  • while语句

    1
    2
    while a>5 :
    #while语句

Context Manager 机制

  • Python中的Context Manager指支持上下文管理的对象(这里的对象指较为广义的对象,可以是一个object,也可以是一个函数等),这种对象可以通过with语句块进行自动的上下文管理(进入语句块前和离开语句块后自动进行相应的上下文切换操作)。
  • 常用的实现Context Manager的方式有三种:
    1. 通过面向对象的形式,实现类的__enter__() and __exit__()成员函数(最常用);
    2. 使用生成器generator;
    3. 使用contextmanager decorator。
  • 下面重点介绍第一种方式的实现。

With Statement

  • python中的with语句是主要用来简化try...finally语句块,以保证clean-up code一定会被执行。
  • with语句本质上是一个资源管理语句(或者说上下文切换语句),其特别之处在于可以在语句块开始和退出时自动执行一些功能,这就特别适合那些需要正确初始化和释放的资源,如文件操作、tensorflow的session操作等。
  • 因此当代码涉及到一些复杂的资源时,就可以把代码放到with语句块中,这样就可以保证资源能被正确地初始化和结束/正确地进行上下文切换。
  • 如何实现with语句:上文所指的“资源”本质就是class,通过定义类的成员函数__enter__()__exit__(),就可以实现让with语句自动管理资源的功能。
  • 如何使用with语句:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    with expression [as name]: 
    with-block
    # expression是一个object,as定义了对象在这个语句块中的变量名
    # eg.
    with open('filename', 'r') as f:
    for line in f:
    print('> {}'.format(line))

    with tf.Session() as sess:
    # ...

函数

python函数命名规则

  • 单下划线:_xx,表示私有属性/函数。
  • 双下划线:__xx,表示来避免子类的重写。
  • 前后各双下划线:__xx__,表示这是python内部调用的函数,如__init__, __str__, __add__, __sub__等,平常我们不需要管他们,python内部会进行调用。

函数中的*与**

python里星号*主要在函数定义和函数调用的时候使用

  • 函数定义时:
    使用单个*会将所有的位置参数,以元组(tuple)的形式传入供函数使用;
    使用两个**会将所有的关键字参数,以字典(dict)的形式传入供函数使用;
  • 函数调用时:
    在iterator前加一个星号:把迭代器中的所有元素解包(unpack)变成位置参数;
    在dict前加一个星号:仅把字典的键解包成位置参数(不常用);
    在dict前加两个星号:把字典的键值对都解包,变成关键字参数(键值对的形式),通常和函数内**定义的参数一起使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def f1(param1, *param2):
    print(param1)
    print(param2)
    f1(1,2,3,4,5)
    # output
    # 1
    # (2, 3, 4, 5)

    def f2(*param):
    print(param)
    li = [1,2,3,4]
    f2(*li)
    # output
    # (1, 2, 3, 4)

    # 使用**
    def f3(**kwargs):
    print(kwargs)
    d = {'a':'Ass','b':'We','c':'Can'}
    f3(**d)
    # output
    # {'a': 'Ass', 'b': 'We', 'c': 'Can'}
  • 使用*进行解包操作

    1
    2
    3
    4
    5
    6
    7
    8
    >>> x = [1, 2, 3]
    >>> y = [4, 5, 6]
    >>> zipped = zip(x, y)
    >>> list(zipped)
    [(1, 4), (2, 5), (3, 6)]
    >>> x2, y2 = zip(*zip(x, y))
    >>> x == list(x2) and y == list(y2)
    True

文件操作

  • 文件路径
    Windows中文件分隔符为\\, Linux和Mac中是/, 注意区分.
    绝对路径: Mac下绝对路径为/Users/lrrr/Dekstop(Doucuments, Downloads等), 起始为/Users/lrrr.
    相对路径: 相对是指相对于.py文件所在目录的路径, 路径中用.表示向上一级目录(..就是上两级目录).

  • 读取文件

    1
    2
    3
    4
    5
    6
    7
    8
    f = open("test.txt")
    for line in f #读取一行
    print(line)

    f = open("e:\\test.txt")
    f.read(12) #读取12个字符
    f.readline() #读取一行
    f.readlines() #按行读取文件,并存到list中
  • 写文件

    1
    2
    3
    4
    5
    6
    f = open("e:\\test.txt","w")   #先清空文件再写

    f = open("e:\\test.txt","a") #追加写
    f.write(xxx)
    #插入换行
    f.write(xxx+'\n') #win下会自动翻译为\r\n,直接在字符串中写\n没用

闭包(closure)

何为闭包

  • 闭包定义:引用(使用)了自由变量的函数(多是作为嵌套函数(nested function)的形式存在的)。
  • 被引用的自由变量将会和这个函数一同存在(即使已经离开了它的创造环境),因此可认为闭包是由函数和其相关的引用环境所组成的对象。
  • 通常这些被引用的自由变量常被设为一些环境参数(用来设置闭包的某些性质),而闭包这个函数本身的参数可以设置为输入变量,和环境变量一起完成闭包的功能。
  • 何为自由变量:和自由变量相对的概念为“约束变量”,一般来说在函数内部定义的局部变量为自由变量,在外部定义的(又在函数内部使用的)变量称为自由变量(因为没有被“约束”在局部内部)。for example:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // C语言example
    int tripple = 20;
    int global = 10;

    int function(int x) {
    int tripple = 0; // tripple is bound because it is local
    tripple = global * 3; // global is free
    return x + tripple; // x is bound
    }

为什么使用必包

  • 首先要明确一点不使用闭包也可以编程,闭包的存在意义在于提供了一种抽象方式。
  • 闭包是函数式编程的一种特性,其本质上和面向对象是一样的(我们甚至可以将闭包函数和其引用的自由变量整体作为一个对象),都是为了提供抽象。
  • 闭包也经常用于回调函数中,因为闭包本身就是提供了对函数的引用(只不过函数引用了自由变量),因此也是通过后期调用的形式来使用的。

    1
    2
    3
    4
    5
    6
    7
    8
    # 2次函数对象
    def parabola(a, b, c):
    def para(x):
    return a*x**2 + b*x +c
    return para # 闭包的要求:返回函数的“引用”(即不带任何参数的函数名)

    p = parabola(2, 3, 4) # 指定2次函数的a b c参数
    print(p(5)) # 输入5,返回这个2次函数的输出
  • 上面代码中,p = parabola(2, 3, 4)定义了y=2x^2+3x+4这个二次函数对象,这样要计算x=5时的函数值,只需要p(5)即可,而无需重复输入抛物线的a, b, c参数。

  • 上述功能完全可以通过类(面向对象)来实现,因此我们说闭包和面向对象本质是一样的,但闭包更简洁(内存开销也更小?),因此对于一些功能简单的类推荐使用闭包来实现。

闭包不等于lamada函数

  • lambda 函数,一般也称为匿名函数,它允许我们定义一个函数,同时不为它命名。相信用过 Javascipt 的同学们已经很熟悉了,因为它经常出现在回调函数里。
  • 由于现今的主流语言中,lambda 与闭包经常同时出现,使得许多人将二者等同对待,但实际情况并非如此,它们本是两个独立的概念。当前lambda和闭包共同使用的场景多是:使用lambada表达式来定义嵌套函数来产生闭包,这是闭包最简单的一种产生方式(无需专门定义一个函数再嵌套一个函数了)。
  • 题外话,使用lambda的好处:1. 最大的好处就是方便书写,方便修改。例如回调函数使用了 lambda 函数,就相当于直接将回调的逻辑写在了需要使用它的地方,这样当逻辑需要修改时,就不需要首先找到函数定义的位置再去修改,更加方便。2. 还有一个好处就是不用绞尽脑汁想函数名了:)
  • 匿名函数的内容应该是很简单、简短的,如果复杂的话,更推荐的是重新定义一个函数。

Python面向对象

如何定义类

类和函数一样,必须先定义再使用,

1
2
3
4
5
6
class ClassName:
<statement-1>
.
.
.
<statement-N>

注:python2中class定义的写法为class ClassName(object):,之所以加object是要即成object类,而在python3中类默认继承object类,所以就不用加了。

类变量 & 实例变量

  • Class Variables:类变量,用于描述在类的所有实例中通用的数据。定义在类的成员方法之外,所有类的实例都可以访问其值,即类和实例都可以对变量的进行引用;但赋值操作只能通过类来完成,实例.class_variable = xxx的赋值操作只会在实例内生成一个新变量,而不会修改类变量的值。
  • Instance Variables:实例变量,用于描述针对每个实例的特定变量。通过self.xxx的形式定义,因为有self.前缀,所以既可以定义在函数内也可在函数外,都会处于相同的namespace下。
  • 关于Class的namespace:一个class定义后,通常会有三个namespace scope:
    1. 类scope:定义在成员函数外部,没有self.前缀的变量,属于类变量(所有成员函数也属于类scope),这部分内容类和实例都可以访问;
    2. 类成员函数scope:定义在成员函数内部,没有self.前缀的变量,属于函数的局部变量,只在函数内起作用;
    3. 实例scope:定义在成员函数内部(因为要传入self参数),必须有self.前缀,属于实例变量,这部分内容只有实例才能访问;

如何定义类属性

eg.

1
2
3
4
@property
def dims(self):
"""Returns a list of Dimensions, or None if the shape is unspecified."""
return self._dims

前面要有关键字@property;定义成函数的形式,把相应的值返回出去。

slice机制

  • python中类可以通过实现__setitem____getitem__方法,来实现以list的形式设置、访问类中的属性。即my_class[0], my_class[1]这种方式。
  • Python还有更强大的slice访问机制,只要是实现了上述方法,可以很方便地访问多个元素,如对数组list而言,有以下功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    array[start:stop]  # items start through stop-1
    array[start:] # items start through the rest of the array
    array[:stop] # items from the beginning through stop-1
    array[:] # a copy of the whole array
    array[start:stop:step] # start through not past stop, by step

    array[-1] # 取最末一项
    array[-2:] # 取最末两项
    array[:-2] # 从开始取到倒数第二项(不包括倒数第二项)

    a[::-1] # 倒序取所有元素
    a[1::-1] # 倒序取前两项(从1开始,倒叙取)
    a[:-3:-1] # 倒叙取后两项(以-3结束,倒叙取)
    a[-3::-1] # 从后往前倒序取所有元素(除最后两项)
  • 还可以实现__iter__方法,实现对类中元素的迭代。

Python Naming Conventions

Naming With Underscores

to be updated: https://dbader.org/blog/meaning-of-underscores-in-python

  • Single Leading Underscore: _var
  • Single Trailing Underscore: var_
  • Double Leading Underscore: __var
  • Double Leading and Trailing Underscore: __var__:前后都有双下划线的命名方式,通常用于有特殊用处的的成员方法,如__init__()用于类的构造函数,__setitem__()__getitem__()用于实现sclice机制。最好不要自己定义这种命名的函数,说不定就和哪个系统保留函数冲突了。

Python变量的命名空间机制

变量的绑定(binding)机制

  • 在Python中,所有的变量名都是引用(reference)/指针(pointer),并不是真正的对象,其只是将名字绑定到对象上,可以将其理解为指针(指向某个内存地址)。
  • 因此变量的重新赋值操作,即a = b,只是将变量名绑定到了另一个对象上了而已,并没有更改原对象的值(况且有些类型的值根本就不可更改:imutable types)。
  • 因此函数的参数传递也都是传引用,python中根本就没有“传值”一说。
  • Python有垃圾回收机制,会管理所有已分配内存的对象,当对象没有变量名引用的时候,系统就会自动将其释放掉。

引用、赋值操作与namescope

  • 在Python中,对变量来说总共就有两种操作,一种是引用操作(reference),即直接使用变量,属于读操作;一种是赋值(assignment)操作,即a = 123,属于写操作。
  • 对于赋值操作(assignment),其会更改当前local namespace下变量所绑定的值,local namespace下变量不存在就新建一个变量。因此赋值操作怎么也不会改变高层次的namespace下的变量(除非指定nonglobal/global statement,或通过attribute的形式(module_name.variable)明确指定变量);
  • 而对于引用操作(reference),则会按namespace从小到大来搜索相应的变量(local->nonlocal->global->built-in),来确定变量所指向的对象。如果都找不到,程序即会出错。

可变类型和不可变类型

  • 在python中,类型分为可变(mutable types)和不可变(imutable types)两大类,不可变类型包括int, float, bool, str, tuple, unicode,可变类型包括list, set, dict,以及大部分的自定义class都是可变类型。
  • 这里说的可变和不可变,指的是已分配到内存上的值是否可以再更改。对于不可变类型,如int,一旦分配就不能再更改,想要新的值只能重新分配;对于可变类型,可通过其成员方法来改变其值(如list.append()方法)。
  • 注意:对于不可变类型,通过赋值(assignment)的方式是无法改变原有内存上的值的,其只是将变量名绑定到了另一个内存地址上了而已。可变类型的值的修改只能通过特定的函数。
    1
    2
    3
    4
    5
    6
    list_x = [1, 2, 3]
    list_y = list_x
    list_y = [4, 5, 6]
    print(list_x) # 输出依旧是[1, 2, 3]
    list_y.append(4)
    print(list_x) # 输出为[1, 2, 3, 4]

变量的命名空间(Namespaces)

  • A namespace is a mapping from names to objects。即命名空间就是一个映射,从名字到其所指代对象的映射。
  • 具体来说,定义在module中的名字、python的内置函数(eg. abs())都属于namespace。
  • 其实只要有变量名/函数名出现,就是namespace,python的变量绑定机制就是namespace所指的映射。

命名空间的四种范围(four scopes of namespace)

每个namespcace都会有一个范围(scope),以此来保证名字的可重用性(相同的名字在不同命名空间中可以指向不同的对象)。命名空间按从小到大可分为以下四种:

  1. Local Scopes:局部命名空间。在函数内即对应当前函数的命名空间,若没有global/nonlocal声明,在函数内赋值的变量默认都属于这个命名空间;在模块内即对应全局命名空间。
  2. Scopes of enclosing functions:即外层函数的命名空间,对应non-local statement;
  3. Global Scopes:全局命名空间,指module-level的命名空间,有两种声明方式:a. 在module最顶层声明的变量默认都属于全局空间(全局空间其实就相当于module的局部空间);b. 在函数内部,通过global statement声明的变量,会映射到全局变量空间中去寻找其指向的对象,即到module-level去寻找对象;
  • 注1:module.name方式调用的都属于这个命名空间。
  • 注2:直接在写在模块最顶层的变量,最终都会变为__main__ module的一部分,因此他们属于__main__ module的全局空间。
  1. Built-in Scopes:内置命名空间,指python的内置变量、函数。
  • 给定一个变量名,必须要确定其所属的命名空间才可知道其指向的对象,赋值操作中的变量默认都是属于local namespace。
  • 因此外层命名空间的变量对内层函数来说都是只可读取、不可赋值的(因为若想通过a = b来更改变量所绑定值,只会创造一个属于local scope的新变量)。因此若想重写外层变量,只能通过non-localglobal statement。
  • 对于变量的引用操作(reference),会按namespace从小到大来搜索(local->nonlocal->global->built-in),以确定变量所指向的对象。如果都找不到,程序即会出错。
  • example code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    def scope_test():
    def do_local():
    spam = "local spam"

    def do_nonlocal():
    nonlocal spam
    spam = "nonlocal spam"

    def do_global():
    global spam
    spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

    scope_test()
    print("In global scope:", spam)

    # Output:
    # After local assignment: test spam
    # After nonlocal assignment: nonlocal spam
    # After global assignment: nonlocal spam
    # In global scope: global spam

    # 默认赋值是local的,所以不会更改scope_test()的spam;
    # nonlocal声明将命名空间改为了scope_test函数的级别,因此spam变量指向了正确的对象,修改成功;
    # global声明则将命名空间改为了module-level,因此会创造一个新的module-level的spam,而非修改原先的。

Class的命名空间

类变量的命名空间是高于实例变量的,因此实例访问类变量也是可以访问的(实例内部没有,因此就会向上查找);但实例无法对类变量进行赋值操作,一旦赋值就会生成新的实例变量覆盖掉类变量了。

其他一些杂七杂八

Mac中的python

  • Mac自带python2,且不能卸载 (一部分系统功能依赖这个python)。
  • 因此装python3后,系统中就会带有两个版本python, python命令就不能只写python了,需要用python2python3来区分, pip命令也是pip2 & pip3(所以推荐使用annaconda来管理python版本,切换python版本后自动修改python相关环境变量)。
  • 但可以指定python的默认解释器位置,pip也会根据python解释器而改变(具体方法待补充)。

关于面向对象

  • 何为attritube:attribute指跟在点后面的名字,如z.realreal即是z的attribute。
  • 类(Class)、对象(Object)和实例(Instance)区别:
    对象感觉是一个更为抽象的概念,如面向对象编程;
    类是一类方法和属性的集合,可理解为一个抽象的模版。
    实例是类的具体实现。
    接口:抽象程度更高的类,仅仅指明了应该有什么样的功能,没有规定具体实现。

Python如何在代码如何换行

  • Python并不使用特定的符号来标记一个语句的结束(如c++中的分号),而是通过检测回车符enter来判断,因此最好在一行内写完所有代码。
  • 但如果遇到一行写不完的情况,有两种换行方法:
  1. 在该行代码末尾加反斜杠\,即可在下一行接着写(但要注意\必须是一行的最末字符,其后不能再有任何字符,包括空格);
  2. 在括号(), {}, []内部不需要特别加\,在项与项之间可直接enter进行换行(但不能把单个项拆开)。
  • 注:换行后最好缩进四个空格/tab,以增强可读性。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    test1 = "123456"
    test2 = "123\
    456" # 与test相同

    test3 = ("123", "456")
    test4 = ("123",
    "456") # 正确,与test3相同

    #test4 = ("123", "4
    # 56")
    #错误,不能用enter将单个项拆开

    test5 = ("123", "4\
    56") # 正确

关于编译型和解释型语言

摘自Python是解释性语言吗? 直到看到有 python py、pyc、pyo、pyd 文件

  • 计算机是不能够识别高级语言的,所以当我们运行一个高级语言程序的时候,就需要一个“翻译机”来从事把高级语言转变成计算机能读懂的机器语言的过程。这个过程分成两类,第一种是编译,第二种是解释。
  • 编译型语言在程序执行之前,先会通过编译器对程序执行一个编译的过程,把程序转变成机器语言。运行时就不需要翻译,而直接执行就可以了。最典型的例子就是C语言。
  • 解释型语言就没有这个编译的过程,而是在程序运行的时候,通过解释器对程序逐行作出解释,然后直接运行这一行代码,最典型的例子是Ruby。
  • 通过以上的例子,我们可以来总结一下解释型语言和编译型语言的优缺点,因为编译型语言在程序运行之前就已经对程序做出了“翻译”,所以在运行时就少掉了“翻译”的过程,所以效率比较高。但是我们也不能一概而论,一些解释型语言也可以通过解释器的优化来在对程序做出翻译时对整个程序做出优化(但总体上还是编译型语言效率更高)。
  • 然而当前随着Java/Python等基于虚拟机的语言的兴起,我们往往不能把语言纯粹地分成解释型和编译型这两种。用Java来举例,Java首先是通过编译器编译成字节码文件,然后在运行时通过解释器给解释成机器文件。所以我们说Java是一种先编译后解释的语言。

    关于跨平台性的理解

    摘自解释型语言可以跨平台而编译型语言不行
  • 对于解释型语言跨平台而编译型语言不能跨平台,网上的解释都是:编译器需依靠平台,而解释型语言依靠不同平台的解释器就可以实现跨平台。我就想如果在不同平台上装对应的编译器,不也可以实现跨平台吗?
  • 首先,其实说编译型语言不能跨平台是不准确的,理论上只要在不同平台装上对应的编译器,编译型语言(比如c语言)也是跨平台的。但是因为编译型语言与平台联系比较紧密、较为底层(操作系统都是用编译型语言编写的,因为编译型语言的特性——快),因此编译性语言往往会使用很多和平台相关的方法。比如,在Windows用c写了调用Windows API的程序,在Linux系统的编译器上编译自然会出错。因此如果能避免这些特殊的使用,编译型语言理论上也是跨平台的。
  • 而对于解释型语言就很少存在这个问题,其先天就和和平台底层接触较少,所以用解释型语言写出来的程序,只要通过不同的解释器/虚拟机解释执行,就可以在对应不同的平台上使用。

Post Date: 2018-09-13

版权声明: 本文为原创文章,转载请注明出处